#include <stdarg.h>
#include <stdlib.h>

#define NIT_SHORT_NAMES
#include <nit/list.h>
#include <nit/macros.h>
#include <nit/palloc.h>

#include "des.h"
#include "cell.h"

Inb_cell *
inb_cell_new(void *prev, void *next, void *here)
{
	Inb_cell *cell;
	Inb_des *des = inb_palloc(cell);

	pcheck(des, NULL);
	des->type = CELL;
	cell = inb_thing(des);
	LIST_APP(cell, NULL);
	DLIST_PREP(cell, NULL);
	inb_prep(cell, prev);
	inb_app(cell, next);
	cell->here = here;
	return cell;
}

void *
inb_car(void *dat)
{
	if (dat && inb_type(dat) == CELL)
		return ((Inb_cell *) dat)->here;

	return NULL;
}

void *
inb_cdr(void *dat)
{
	if (dat && inb_type(dat) == CELL)
		return NIT_LIST_NEXT(dat, void);

	return NULL;
}

void *
inb_rcdr(void *dat)
{
	if (dat && inb_type(dat) == CELL)
		return NIT_DLIST_PREV(dat, void);

	return NULL;
}

void
inb_app(Inb_cell *cell, void *next)
{
	void *old_next = LIST_NEXT(cell, void);
	void *prev;

	if (old_next && inb_type(old_next) == CELL)
		DLIST_PREP(old_next, NULL);

	if (next && inb_type(next) == CELL) {
		prev = DLIST_PREV(next, void);

		if (prev && inb_type(prev) == CELL)
			LIST_APP(prev, NULL);

		DLIST_PREP(next, cell);
	}

	LIST_APP(cell, next);
}

void
inb_prep(Inb_cell *cell, void *prev)
{
	void *old_prev = DLIST_PREV(cell, void);
	void *next;

	if (old_prev && inb_type(old_prev) == CELL)
		LIST_APP(old_prev, NULL);

	if (prev && inb_type(prev) == CELL) {
		next = LIST_NEXT(prev, void);

		if (next && inb_type(next) == CELL)
			DLIST_PREP(next, NULL);

		LIST_APP(prev, cell);
	}

	DLIST_PREP(cell, prev);
}

Inb_cell *
inb_cell_va(size_t len, ...)
{
	Inb_cell *list;
	Inb_cell *cell;
	void *dat;
	va_list vl;

	if (len < 3)
		return NULL;

	va_start(vl, len);
	dat = va_arg(vl, void *);
	cell = list = inb_rcons(va_arg(vl, void *), dat);

	for (len -= 2; len > 1; --len)
		cell = inb_rcons(va_arg(vl, void *), cell);

	inb_app(cell, va_arg(vl, void *));
	va_end(vl);
	return list;
}
